package ua.lviv.franko.solvers.twoDimention;

import java.util.ArrayList;
import java.util.List;

import ua.lviv.franko.integration.Gauss2D;
import ua.lviv.franko.integration.IFEMFunction2D;
import ua.lviv.franko.integration.IFunction2D;
import ua.lviv.franko.solvers.stressanalysis.twoDimention.ElementMatrixForStress;
import ua.lviv.franko.solvers.stressanalysis.twoDimention.fem.AbstractStressElement;
import ua.lviv.franko.triangulation.SaveStructures.Node;

public class LinearTriangularElement extends AbstractStressElement {

	boolean								isAactive				= true;
	boolean								isBactive				= true;
	boolean								isCactive				= true;

	protected double					S2;

	protected double					ai, aj, ak;
	protected double					bi, bj, bk;
	protected double					ci, cj, ck;

	public static int					QuadraturePointsNumber	= 20;
	private Gauss2D						gau;

	// triangle data
	protected Node						A;
	protected Node						B;
	protected Node						C;

	public IFEMFunction2D				N1;
	public IFEMFunction2D				N2;
	public IFEMFunction2D				N3;

	ArrayList<Integer>					pos;
	// results
	protected ElementMatrixForStress	data;

	public LinearTriangularElement(int ANum, int BNum, int CNum, final Node A, final Node B, final Node C, double k1, double k2, double k3, IFunction2D f1Function,
			IFunction2D f2Function) {
		super(k1, k2, k3, f1Function, f2Function);
		pos = new ArrayList<Integer>();
		pos.add(ANum);
		pos.add(BNum);
		pos.add(CNum);

		this.A = A;
		this.B = B;
		this.C = C;

		ai = B.x * C.y - C.x * B.y;
		aj = C.x * A.y - A.x * C.y;
		ak = A.x * B.y - B.x * A.y;

		bi = B.y - C.y;
		bj = C.y - A.y;
		bk = A.y - B.y;

		ci = C.x - B.x;
		cj = A.x - C.x;
		ck = B.x - A.x;

		double a = Math.sqrt((ck) * (ck) + (bk) * (bk)); // a-b
		double b = Math.sqrt((ci) * (ci) + (bi) * (bi));
		double c = Math.sqrt((cj) * (cj) + (bj) * (bj));
		double p = (a + b + c) / 2.0;
		S2 = Math.sqrt(p * (p - a) * (p - b) * (p - c)) * 2.0;

		N1 = new IFEMFunction2D() {

			@Override
			public double calculate(double x, double y) {
				if (!isInTriangle(A, B, C, x, y)) {
					return 0.0;
				}
				return (ai + bi * x + ci * y) / S2;
			}

			@Override
			public double calculateDerivatyX(double x, double y) {
				if (!isInTriangle(A, B, C, x, y)) {
					return 0.0;
				}
				return (bi) / S2;
			}

			@Override
			public double calculateDerivatyY(double x, double y) {
				if (!isInTriangle(A, B, C, x, y)) {
					return 0.0;
				}
				return (ci) / S2;
			}

			@Override
			public double getXi() {
				return A.x;
			}

			@Override
			public double getYi() {
				return A.y;
			}

			@Override
			public Node getNode() {
				// TODO Auto-generated method stub
				return null;
			}
		};
		N2 = new IFEMFunction2D() {

			@Override
			public double calculate(double x, double y) {
				if (!isInTriangle(A, B, C, x, y)) {
					return 0.0;
				}
				return (aj + bj * x + cj * y) / S2;
			}

			@Override
			public double calculateDerivatyX(double x, double y) {
				if (!isInTriangle(A, B, C, x, y)) {
					return 0.0;
				}
				return (bj) / S2;
			}

			@Override
			public double calculateDerivatyY(double x, double y) {
				if (!isInTriangle(A, B, C, x, y)) {
					return 0.0;
				}
				return (cj) / S2;
			}

			@Override
			public double getXi() {
				return B.x;
			}

			@Override
			public double getYi() {
				return B.y;
			}

			@Override
			public Node getNode() {
				// TODO Auto-generated method stub
				return null;
			}
		};
		N3 = new IFEMFunction2D() {

			@Override
			public double calculate(double x, double y) {
				if (!isInTriangle(A, B, C, x, y)) {
					return 0.0;
				}
				return (ak + bk * x + ck * y) / S2;
			}

			@Override
			public double calculateDerivatyX(double x, double y) {
				if (!isInTriangle(A, B, C, x, y)) {
					return 0.0;
				}
				return (bk) / S2;
			}

			@Override
			public double calculateDerivatyY(double x, double y) {
				if (!isInTriangle(A, B, C, x, y)) {
					return 0.0;
				}
				return (ck) / S2;
			}

			@Override
			public double getXi() {
				return C.x;
			}

			@Override
			public double getYi() {
				return C.y;
			}

			@Override
			public Node getNode() {
				// TODO Auto-generated method stub
				return null;
			}
		};
		baseFunctions.add(N1);
		baseFunctions.add(N2);
		baseFunctions.add(N3);

		gau = new Gauss2D(QuadraturePointsNumber);
	}

	@Override
	public double calculateU(double x, double y, double[] vals) {
		if (!isInTriangle(A, B, C, x, y)) {
			return 0.0;
		}
		return N1.calculate(x, y) * vals[pos.get(0) * 2] + N2.calculate(x, y) * vals[pos.get(1) * 2] + N3.calculate(x, y) * vals[pos.get(2) * 2];
	}

	@Override
	public double calculateV(double x, double y, double[] vals) {
		if (!isInTriangle(A, B, C, x, y)) {
			return 0.0;
		}
		return N1.calculate(x, y) * vals[pos.get(0) * 2 + 1] + N2.calculate(x, y) * vals[pos.get(1) * 2 + 1] + N3.calculate(x, y) * vals[pos.get(2) * 2 + 1];
	}

	@Override
	public ElementMatrixForStress getAll() {
		return data;
	}

	@Override
	public ArrayList<Integer> getPos() {
		return pos;
	}

	private boolean isInTriangle(Node A, Node B, Node C, double x, double y) {
		double Str = S(A.x, A.y, B.x, B.y, C.x, C.y);
		double Str1 = S(x, y, B.x, B.y, C.x, C.y);
		double Str2 = S(A.x, A.y, x, y, C.x, C.y);
		double Str3 = S(A.x, A.y, B.x, B.y, x, y);

		return Math.abs(Str - (Str1 + Str2 + Str3)) < 1E-10;
	}

	private double S(double x1, double y1, double x2, double y2, double x3, double y3) {
		return Math.abs((x2 * y3 + x1 * y2 + y1 * x3 - (y2 * x3 + y1 * x2 + x1 * y3)) / 2.0);
	}

	@Override
	protected ElementMatrixForStress calculateSmallData(int i, int j) {
		ElementMatrixForStress res = new ElementMatrixForStress(getNodesNumbers());
		res.matrix = this.calculateSmallMatrix(i, j);
		res.vector = this.calculateSmallVector(i);
		return res;
	}

	@Override
	protected double[][] calculateSmallMatrix(int i, int j) {
		double[][] result = new double[2][2];

		final IFunction2D fi = baseFunctions.get(i);
		final IFunction2D fj = baseFunctions.get(j);

		result[0][0] = gau.gaussOnTriangle(A, B, C, new IFunction2D() {

			@Override
			public double calculate(double x, double y) {
				return fi.calculateDerivatyX(x, y) * fj.calculateDerivatyX(x, y) * k1 + fi.calculateDerivatyY(x, y) * fj.calculateDerivatyY(x, y) * k3;
			}

			@Override
			public double calculateDerivatyX(double x, double y) {

				return 0;
			}

			@Override
			public double calculateDerivatyY(double x, double y) {

				return 0;
			}
		});

		result[1][1] = gau.gaussOnTriangle(A, B, C, new IFunction2D() {

			@Override
			public double calculate(double x, double y) {
				return fi.calculateDerivatyX(x, y) * fj.calculateDerivatyX(x, y) * k3 + fi.calculateDerivatyY(x, y) * fj.calculateDerivatyY(x, y) * k1;
			}

			@Override
			public double calculateDerivatyX(double x, double y) {

				return 0;
			}

			@Override
			public double calculateDerivatyY(double x, double y) {

				return 0;
			}
		});

		result[1][0] = gau.gaussOnTriangle(A, B, C, new IFunction2D() {

			@Override
			public double calculate(double x, double y) {
				return fj.calculateDerivatyX(x, y) * fi.calculateDerivatyY(x, y) * k2 + fj.calculateDerivatyY(x, y) * fi.calculateDerivatyX(x, y) * k3;
			}

			@Override
			public double calculateDerivatyX(double x, double y) {

				return 0;
			}

			@Override
			public double calculateDerivatyY(double x, double y) {

				return 0;
			}
		});

		result[0][1] = result[1][0];

		return result;
	}

	@Override
	protected double[] calculateSmallVector(int i) {
		double[] result = new double[2];

		final IFunction2D fi = baseFunctions.get(i);

		result[0] = gau.gaussOnTriangle(A, B, C, new IFunction2D() {

			@Override
			public double calculate(double x, double y) {
				return fi.calculate(x, y) * f1Function.calculate(x, y);
			}

			@Override
			public double calculateDerivatyX(double x, double y) {

				return 0;
			}

			@Override
			public double calculateDerivatyY(double x, double y) {

				return 0;
			}
		});
		result[1] = gau.gaussOnTriangle(A, B, C, new IFunction2D() {

			@Override
			public double calculate(double x, double y) {
				return fi.calculate(x, y) * f2Function.calculate(x, y);
			}

			@Override
			public double calculateDerivatyX(double x, double y) {

				return 0;
			}

			@Override
			public double calculateDerivatyY(double x, double y) {

				return 0;
			}
		});

		return result;
	}

	@Override
	public List<Integer> getNodesNumbers() {
		return getPos();
	}

	@Override
	public double calculateSigma11(double x, double y, double[] vals) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public double calculateSigma12(double x, double y, double[] vals) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public double calculateSigma22(double x, double y, double[] vals) {
		// TODO Auto-generated method stub
		return 0;
	}

}
